﻿#include "pch.h"
#include "Utility.h"
#include <string>
#include <fstream>
#include <iostream>
#include <thread>
#include <regex>
#include <unordered_set>
#include <codecvt>
#include <mutex>
#include <atomic>
#include <algorithm>
#include "../includes/libxl-win-4.4.1/include_cpp/libxl.h"
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#undef min
#undef max
#include "Logging.h"
#include "VersionControl.h"

//#define RUNWITHCMD
#define RUNWITHPROCESS

std::string Utility::WorkingDirectory;
std::string Utility::tortoiseSVNPath;
std::string Utility::comparePath;
std::string Utility::tortoiseGitPath;
std::string Utility::svnCliPath;
std::string Utility::gitCliPath;
VcsType Utility::currentVcsType = VcsType::SVN;
std::string Utility::lastSVNTarget;
std::string Utility::lastSVNSuffix;
std::string Utility::lastMergeSource;
std::string Utility::lastMergeTarget;
std::string Utility::lastMergeSuffix;
std::string Utility::lastSearchExcelProj;
std::string Utility::svnAuthor;
std::string Utility::svnLogPath;

std::map<std::string, std::string> Utility::projPathMap;
std::map<std::string, std::string> Utility::urlMap;
std::map<std::string, std::string> Utility::suffixMap;
std::vector<winrt::hstring> Utility::offenUseExcelList;


// 将 SYSTEMTIME 转换为 std::chrono::system_clock::time_point
static std::chrono::system_clock::time_point SystemTimeToTimePoint(const SYSTEMTIME& st)
{
	FILETIME ft;
	SystemTimeToFileTime(&st, &ft);

	ULARGE_INTEGER ull;
	ull.LowPart = ft.dwLowDateTime;
	ull.HighPart = ft.dwHighDateTime;

	return std::chrono::system_clock::time_point(std::chrono::duration_cast<std::chrono::system_clock::duration>(
		std::chrono::microseconds(ull.QuadPart / 10)));
}

// 将 std::chrono::system_clock::time_point 转换为 SYSTEMTIME
static SYSTEMTIME TimePointToSystemTime(const std::chrono::system_clock::time_point& tp)
{
	ULARGE_INTEGER ull;
	ull.QuadPart = std::chrono::duration_cast<std::chrono::microseconds>(tp.time_since_epoch()).count() * 10;

	FILETIME ft;
	ft.dwLowDateTime = ull.LowPart;
	ft.dwHighDateTime = ull.HighPart;

	SYSTEMTIME st;
	FileTimeToSystemTime(&ft, &st);
	return st;
}

// 将字符串转换为小写
static std::string ToLower(const std::string& str)
{
	std::string lowerStr = str;
	std::transform(lowerStr.begin(), lowerStr.end(), lowerStr.begin(), ::tolower);
	return lowerStr;
}

static std::wstring ToLower(const std::wstring& str)
{
	std::wstring lowerStr = str;
	std::transform(lowerStr.begin(), lowerStr.end(), lowerStr.begin(), ::tolower);
	return lowerStr;
}


// 判断字符串是否包含子字符串（不区分大小写）
static bool ContainsIgnoreCase(const std::string& str, const std::string& subStr)
{
    const std::string lowerStr = ToLower(str);
    const std::string lowerSubStr = ToLower(subStr);
    return lowerStr.find(lowerSubStr) != std::string::npos;
}

static bool ContainsIgnoreCase(const std::wstring& str, const std::wstring& subStr)
{
    const std::wstring lowerStr = ToLower(str);
    const std::wstring lowerSubStr = ToLower(subStr);
    return lowerStr.find(lowerSubStr) != std::wstring::npos;
}


static std::wstring string_to_wstring(const std::string& str) {
	int size_needed = MultiByteToWideChar(CP_ACP, 0, str.c_str(), (int)str.size(), NULL, 0);
	std::wstring wstr(size_needed, 0);
	MultiByteToWideChar(CP_ACP, 0, str.c_str(), (int)str.size(), &wstr[0], size_needed);
	return wstr;
}

static winrt::hstring ansi_to_hstring(const std::string& str)
{
	// 获取转换后的宽字符长度
	int wstr_length = MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, nullptr, 0);
	if (wstr_length == 0)
	{
		throw std::runtime_error("Failed to convert ANSI string to wide string.");
	}

	// 分配宽字符缓冲区
	std::wstring wstr(wstr_length, 0);

	// 执行转换
	int result = MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, &wstr[0], wstr_length);
	if (result == 0)
	{
		throw std::runtime_error("Failed to convert ANSI string to wide string.");
	}

	// 去掉末尾的空字符
	wstr.resize(wstr_length - 1);

	return winrt::hstring(wstr);
}


#pragma region Initialization

// 初始化工作目录
static void InitWorkingDirectory(std::string& toApplay)
{
	// 获取程序的完整路径
	char buffer[MAX_PATH];
	GetModuleFileNameA(NULL, buffer, MAX_PATH);

	// 获取程序所在的目录
	std::string programDir = buffer;
	size_t lastBackslashIndex = programDir.find_last_of("\\");
	toApplay = programDir.substr(0, lastBackslashIndex);

    LOG_DEBUG(toApplay);
}

// 创建xml配置解析器
std::unordered_map<std::string, std::function<void(TiXmlElement*)>> Utility::GetElementHandlers()
{
	std::unordered_map<std::string, std::function<void(TiXmlElement*)>> handlers;

	auto SetStringCfg = [](const char* value, std::string& target)
		{
			if (value != nullptr)
			{
				target = value;
			}
		};

	// TortoiseSVNPath
	handlers["tortoiseSVN"] = [SetStringCfg](TiXmlElement* element)
		{
			const char* path = element->GetText();
			SetStringCfg(path, Utility::tortoiseSVNPath);
		};

	// ComparePath
	handlers["ComparePath"] = [SetStringCfg](TiXmlElement* element)
		{
			const char* path = element->GetText();
			SetStringCfg(path, Utility::comparePath);
		};

	// lastSVNTarget
	handlers["lastSVNTarget"] = [SetStringCfg](TiXmlElement* element)
		{
			const char* path = element->GetText();
			SetStringCfg(path, Utility::lastSVNTarget);
		};

	// lastSVNSuffix
	handlers["lastSVNSuffix"] = [SetStringCfg](TiXmlElement* element)
		{
			const char* path = element->GetText();
			SetStringCfg(path, Utility::lastSVNSuffix);
		};

	// lastMergeSource
	handlers["lastMergeSource"] = [SetStringCfg](TiXmlElement* element)
		{
			const char* path = element->GetText();
			SetStringCfg(path, Utility::lastMergeSource);
		};

	// lastMergeTarget
	handlers["lastMergeTarget"] = [SetStringCfg](TiXmlElement* element)
		{
			const char* path = element->GetText();
			SetStringCfg(path, Utility::lastMergeTarget);
		};

	// lastMergeSuffix
	handlers["lastMergeSuffix"] = [SetStringCfg](TiXmlElement* element)
		{
			const char* path = element->GetText();
			SetStringCfg(path, Utility::lastMergeSuffix);
		};

	// lastSelect
	handlers["lastSearchExcelProj"] = [SetStringCfg](TiXmlElement* element)
		{
			const char* path = element->GetText();
			SetStringCfg(path, Utility::lastSearchExcelProj);
		};

	// svnAuthor
	handlers["svnAuthor"] = [SetStringCfg](TiXmlElement* element)
		{
			const char* path = element->GetText();
			SetStringCfg(path, Utility::svnAuthor);
		};

	// projPath
	handlers["projPath"] = [SetStringCfg](TiXmlElement* element)
		{
			// 每个子节点
			for (auto child = element->FirstChildElement(); child != nullptr; child = child->NextSiblingElement())
			{
				auto path = child->Attribute("path");
				auto url = child->Attribute("url");
				auto name = child->GetText();
				projPathMap[name] = path;
				urlMap[name] = url;
			}

		};

	// pathSuffix
	handlers["pathSuffix"] = [SetStringCfg](TiXmlElement* element)
		{
			// 每个子节点
			for (auto child = element->FirstChildElement(); child != nullptr; child = child->NextSiblingElement())
			{
				auto path = child->Attribute("path");
				auto name = child->GetText();
				suffixMap[name] = path;
			}
		};

	// offenUseExcel
	handlers["OffenUse"] = [SetStringCfg](TiXmlElement* element)
		{
			// 每个子节点
			for (auto child = element->FirstChildElement(); child != nullptr; child = child->NextSiblingElement())
			{
				auto name = child->GetText();
				offenUseExcelList.push_back(winrt::to_hstring(name));
			}
		};

	// logging settings
	handlers[LOG_MIN_LEVEL] = [](TiXmlElement* element)
		{
			const char* text = element->GetText();
			if (!text) return;
			std::string s = text;
			Logging::Level lv = Logging::Level::Info;
			if (s == "Debug" || s == "0") lv = Logging::Level::Debug;
			else if (s == "Info" || s == "1") lv = Logging::Level::Info;
			else if (s == "Warn" || s == "2") lv = Logging::Level::Warn;
			else if (s == "Error" || s == "3") lv = Logging::Level::Error;
			Logging::SetMinLevel(lv);
		};
	handlers[LOG_CONSOLE_ENABLED] = [](TiXmlElement* element)
		{
			const char* text = element->GetText();
			if (!text) return;
			Logging::EnableConsole(std::string(text) == "1");
		};
	handlers[LOG_FILE_ENABLED] = [](TiXmlElement* element)
		{
			const char* text = element->GetText();
			if (!text) return;
			Logging::EnableFile(std::string(text) == "1");
		};
	handlers[LOG_FILE_PATH] = [](TiXmlElement* element)
		{
			const char* text = element->GetText();
			if (!text) return;
			Logging::SetFilePath(text);
		};

	// 可选：tortoiseGit
	handlers["tortoiseGit"] = [SetStringCfg](TiXmlElement* element)
		{
			const char* path = element->GetText();
			SetStringCfg(path, Utility::tortoiseGitPath);
		};

	// 可选：vcsType (TortoiseSVN/TortoiseGIT/SVN/GIT)
	handlers["vcsType"] = [](TiXmlElement* element)
		{
			const char* text = element->GetText();
			if (!text) return;
			std::string s = text;
			std::transform(s.begin(), s.end(), s.begin(), ::tolower);
			if (s == "git") Utility::currentVcsType = VcsType::GIT;
			else if (s == "svn") Utility::currentVcsType = VcsType::SVN;
			else if (s == "tortoisegit") Utility::currentVcsType = VcsType::TortoiseGIT;
			else if (s == "tortoisesvn") Utility::currentVcsType = VcsType::TortoiseSVN;
			else Utility::currentVcsType = VcsType::SVN;
		};

	// 可选：svnCli
	handlers["svnCli"] = [SetStringCfg](TiXmlElement* element)
		{
			const char* path = element->GetText();
			SetStringCfg(path, Utility::svnCliPath);
		};
	// 可选：gitCli
	handlers["gitCli"] = [SetStringCfg](TiXmlElement* element)
		{
			const char* path = element->GetText();
			SetStringCfg(path, Utility::gitCliPath);
		};


	return handlers;
}

void Utility::SetXmlCfg(std::string elementName, std::string elementValue)
{
    TiXmlDocument doc;

    const auto xmlPath = Utility::GetConfigXmlPath();

    // 确保父目录存在
    try
    {
        std::filesystem::path p(xmlPath);
        if (p.has_parent_path())
        {
            std::error_code ec;
            std::filesystem::create_directories(p.parent_path(), ec);
        }
    }
    catch (...)
    {
        // ignore
    }

    bool loaded = doc.LoadFile(xmlPath, TIXML_ENCODING_UTF8);
    if (!loaded)
    {
        // 文件不存在或损坏，创建默认根节点
        auto root = new TiXmlElement("config");
        doc.LinkEndChild(root);
    }

    auto root = doc.RootElement();
    if (root == nullptr)
    {
        root = new TiXmlElement("config");
        doc.LinkEndChild(root);
    }

    auto element = root->FirstChildElement(elementName.c_str());
    if (element == nullptr)
    {
        element = new TiXmlElement(elementName.c_str());
        root->LinkEndChild(element);
    }

    while (auto child = element->FirstChild())
    {
        element->RemoveChild(child);
    }
    element->LinkEndChild(new TiXmlText(elementValue.c_str()));

    doc.SaveFile(xmlPath);
}

/// <summary>
/// Utility初始化
/// </summary>
void Utility::Init()
{
	InitWorkingDirectory(WorkingDirectory);
	InitXmlCfg();

    // 若未配置日志文件路径，则设置默认路径并启用文件输出（Release 也生效）
    EnsureDefaultLoggingConfig();

	wchar_t tempPath[MAX_PATH];
	GetTempPath(MAX_PATH, tempPath);
	// 将宽字符路径转换为 std::string
	char tempPathA[MAX_PATH];
	WideCharToMultiByte(CP_ACP, 0, tempPath, -1, tempPathA, MAX_PATH, NULL, NULL);

	svnLogPath = std::string(tempPathA) + "svn_log.txt";
}

std::string Utility::GetConfigXmlPath()
{
#ifdef DEBUG
    return GetWorkingDirectory() + "\\" + ConfigXmlName;
#else
    return ConfigXmlName;
#endif // DEBUG
}


/// <summary>
/// config.xml配置文件初始化
/// </summary>
void Utility::InitXmlCfg()
{
	TiXmlDocument doc;

	auto xmlPath = Utility::GetConfigXmlPath();
	bool result = doc.LoadFile(xmlPath, TIXML_ENCODING_UTF8);
    LOG_INFO(xmlPath);
	if (!result)
	{
        LOG_ERROR("Load xml file failed!");
		return;
	}

	auto root = doc.RootElement();
	if (root == nullptr)
	{
        LOG_ERROR("Root is null!");
		return;
	}

    // 读取前先清空内存中的配置，避免重复初始化导致的数据累积
    projPathMap.clear();
    urlMap.clear();
    suffixMap.clear();
    offenUseExcelList.clear();

	auto elementHandlers = GetElementHandlers();
	// 遍历每个一级子节点
	for (auto child = root->FirstChildElement(); child != nullptr; child = child->NextSiblingElement())
	{
		auto it = elementHandlers.find(child->ValueStr());
		if (it != elementHandlers.end())
		{
			it->second(child); // 调用处理函数
		}
	}

#if 0
	for (auto& pair : projPathMap)
	{
        LOG_DEBUG(pair.first.c_str());
        LOG_DEBUG(pair.second.c_str());
	}

	for (auto& pair : urlMap)
	{
        LOG_DEBUG(pair.first.c_str());
        LOG_DEBUG(pair.second.c_str());
	}

	for (auto& pair : suffixMap)
	{
        LOG_DEBUG(pair.first.c_str());
        LOG_DEBUG(pair.second.c_str());
	}

	for (auto& name : offenUseExcelList)
	{
        LOG_DEBUG(name.c_str());
	}

#endif
}

#pragma endregion


#pragma region Xml Config

void Utility::SetLastSVNTarget(const std::string& target)
{
	lastSVNTarget = target;
	SetXmlCfg(LAST_SVN_TARGET, target);
}

void Utility::SetLastSVNSuffix(const std::string& suffix)
{
	lastSVNSuffix = suffix;
	SetXmlCfg(LAST_SVN_SUFFIX, suffix);
}

void Utility::SetLastMergeSource(const std::string& source)
{
	lastMergeSource = source;
	SetXmlCfg(LAST_MERGE_SOURCE, source);
}

void Utility::SetLastMergeTarget(const std::string& target)
{
	lastMergeTarget = target;
	SetXmlCfg(LAST_MERGE_TARGET, target);
}

void Utility::SetLastMergeSuffix(const std::string& suffix)
{
	lastMergeSuffix = suffix;
	SetXmlCfg(LAST_MERGE_SUFFIX, suffix);
}

void Utility::SetLastSearchExcelProj(const std::string& proj)
{
	lastSearchExcelProj = proj;
	SetXmlCfg(LAST_SEARCH_EXCEL_PROJ, proj);
}

void Utility::SetTortoiseSVNPath(const std::string& path)
{
	tortoiseSVNPath = path;
	SetXmlCfg("tortoiseSVN", path);
}

void Utility::SetComparePath(const std::string& path)
{
	comparePath = path;
	SetXmlCfg("ComparePath", path);
}

void Utility::SetSvnAuthor(const std::string& author)
{
	svnAuthor = author;
	SetXmlCfg("svnAuthor", author);
}

void Utility::SetLogMinLevel(Logging::Level level)
{
    Logging::SetMinLevel(level);
    const char* v = "Info";
    switch (level)
    {
    case Logging::Level::Debug: v = "Debug"; break;
    case Logging::Level::Info:  v = "Info";  break;
    case Logging::Level::Warn:  v = "Warn";  break;
    case Logging::Level::Error: v = "Error"; break;
    default: break;
    }
    SetXmlCfg(LOG_MIN_LEVEL, v);
}

void Utility::SetLogConsoleEnabled(bool enabled)
{
    Logging::EnableConsole(enabled);
    SetXmlCfg(LOG_CONSOLE_ENABLED, enabled ? "1" : "0");
}

void Utility::SetLogFileEnabled(bool enabled)
{
    Logging::EnableFile(enabled);
    SetXmlCfg(LOG_FILE_ENABLED, enabled ? "1" : "0");
}

void Utility::SetLogFilePath(const std::string& path)
{
    Logging::SetFilePath(path);
    SetXmlCfg(LOG_FILE_PATH, path);
}

void Utility::SetTortoiseGitPath(const std::string& path)
{
    tortoiseGitPath = path;
    SetXmlCfg("tortoiseGit", path);
}

void Utility::SetSvnCliPath(const std::string& path)
{
    svnCliPath = path;
    SetXmlCfg("svnCli", path);
}

void Utility::SetGitCliPath(const std::string& path)
{
    gitCliPath = path;
    SetXmlCfg("gitCli", path);
}

void Utility::SetVcsType(VcsType type)
{
    currentVcsType = type;
    const char* s = "SVN";
    switch (type)
    {
    case VcsType::TortoiseSVN: s = "TortoiseSVN"; break;
    case VcsType::TortoiseGIT: s = "TortoiseGIT"; break;
    case VcsType::SVN:         s = "SVN"; break;
    case VcsType::GIT:         s = "GIT"; break;
    default: break;
    }
    SetXmlCfg("vcsType", s);
}

void Utility::EnsureDefaultLoggingConfig()
{
    // 若日志文件路径未设置，则采用工作目录/log.txt 并开启文件输出
    if (Logging::GetFilePath().empty())
    {
        const std::string defaultPath = GetWorkingDirectory() + "\\log.txt";
        Logging::SetFilePath(defaultPath);
        SetXmlCfg(LOG_FILE_PATH, defaultPath);
    }
    // 确保文件输出开启
    if (!Logging::IsFileEnabled())
    {
        Logging::EnableFile(true);
        SetXmlCfg(LOG_FILE_ENABLED, "1");
    }
    // 若最小级别未设置，可保持当前；也可在此设为 Info
}

/// <summary>
/// 项目路径配置中是否存在指定名称的项目
/// </summary>
/// <param name="name">名称</param>
bool Utility::IsProjNameExist(const std::string& name)
{
	return projPathMap.find(name) != projPathMap.end();
}

bool Utility::IsSuffixExist(const std::string& suffix)
{
	return suffixMap.find(suffix) != suffixMap.end();
}


void Utility::AddOffenUse(const winrt::hstring& name)
{
    // 去重：若已存在则不重复添加
    if (IsOffenUse(name))
    {
        return;
    }
    offenUseExcelList.push_back(name);

	TiXmlDocument doc;

	auto xmlPath = Utility::GetConfigXmlPath();
	bool result = doc.LoadFile(xmlPath, TIXML_ENCODING_UTF8);

	if (!result)
	{
        LOG_ERROR("Load xml file failed!");
		return;
	}

	auto root = doc.RootElement();
	if (root == nullptr)
	{
        LOG_ERROR("Root is null!");
		return;
	}

    auto element = root->FirstChild(OFFEN_USE_EXCEL);
    if (element == nullptr)
    {
        element = new TiXmlElement(OFFEN_USE_EXCEL);
        root->LinkEndChild(element);
    }

	auto child = new TiXmlElement("use");
	child->LinkEndChild(new TiXmlText(winrt::to_string(name).c_str()));
	element->LinkEndChild(child);

	doc.SaveFile(xmlPath);
}

void Utility::AddProj(const std::string name, const std::string path, const std::string url)
{
	projPathMap[name] = path;
	urlMap[name] = url;

	TiXmlDocument doc;

	auto xmlPath = Utility::GetConfigXmlPath();
	bool result = doc.LoadFile(xmlPath, TIXML_ENCODING_UTF8);

	if (!result)
	{
        LOG_ERROR("Load xml file failed!");
		return;
	}

	auto root = doc.RootElement();
	if (root == nullptr)
	{
        LOG_ERROR("Root is null!");
		return;
	}

    auto element = root->FirstChild("projPath");
    if (element == nullptr)
    {
        element = new TiXmlElement("projPath");
        root->LinkEndChild(element);
    }

	auto child = new TiXmlElement("proj");
	child->SetAttribute("path", path.c_str());
	child->SetAttribute("url", url.c_str());
	child->LinkEndChild(new TiXmlText(name.c_str()));
	element->LinkEndChild(child);

	doc.SaveFile(xmlPath);
}

void Utility::AddSuffix(const std::string suffix, const std::string path)
{
	suffixMap[suffix] = path;

	TiXmlDocument doc;

	auto xmlPath = Utility::GetConfigXmlPath();
	bool result = doc.LoadFile(xmlPath, TIXML_ENCODING_UTF8);

	if (!result)
	{
        LOG_ERROR("Load xml file failed!");
		return;
	}

	auto root = doc.RootElement();
	if (root == nullptr)
	{
        LOG_ERROR("Root is null!");
		return;
	}

    auto element = root->FirstChild("pathSuffix");
    if (element == nullptr)
    {
        element = new TiXmlElement("pathSuffix");
        root->LinkEndChild(element);
    }

	auto child = new TiXmlElement("suffix");
	child->SetAttribute("path", path.c_str());
	child->LinkEndChild(new TiXmlText(suffix.c_str()));
	element->LinkEndChild(child);

	doc.SaveFile(xmlPath);
}

void Utility::RemoveProj(const std::string name)
{
	projPathMap.erase(name);
	urlMap.erase(name);

	TiXmlDocument doc;

	auto xmlPath = Utility::GetConfigXmlPath();
	bool result = doc.LoadFile(xmlPath, TIXML_ENCODING_UTF8);

	if (!result)
	{
        LOG_ERROR("Load xml file failed!");
		return;
	}

	auto root = doc.RootElement();
	if (root == nullptr)
	{
        LOG_ERROR("Root is null!");
		return;
	}

	auto element = root->FirstChild("projPath");
	if (element == nullptr)
	{
		return;
	}

	for (auto child = element->FirstChildElement(); child != nullptr; child = child->NextSiblingElement())
	{
		if (child->ValueStr() == "proj" && child->GetText() == name)
		{
			element->RemoveChild(child);
			break;
		}
	}

	doc.SaveFile(xmlPath);
}

void Utility::RemoveSuffix(const std::string suffix)
{
	suffixMap.erase(suffix);

	TiXmlDocument doc;

	auto xmlPath = Utility::GetConfigXmlPath();
	bool result = doc.LoadFile(xmlPath, TIXML_ENCODING_UTF8);

	if (!result)
	{
        LOG_ERROR("Load xml file failed!");
		return;
	}

	auto root = doc.RootElement();
	if (root == nullptr)
	{
        LOG_ERROR("Root is null!");
		return;
	}

	auto element = root->FirstChild("pathSuffix");
	if (element == nullptr)
	{
		return;
	}

	for (auto child = element->FirstChildElement(); child != nullptr; child = child->NextSiblingElement())
	{
		if (child->ValueStr() == "suffix" && child->GetText() == suffix)
		{
			element->RemoveChild(child);
			break;
		}
	}

	doc.SaveFile(xmlPath);
}

void Utility::RemoveOffenUse(const winrt::hstring& name)
{
	//std::remove(offenUseExcelList.begin(), offenUseExcelList.end(), name);
	offenUseExcelList.erase(std::remove(offenUseExcelList.begin(), offenUseExcelList.end(), name), offenUseExcelList.end());
	TiXmlDocument doc;

	auto xmlPath = Utility::GetConfigXmlPath();
	bool result = doc.LoadFile(xmlPath, TIXML_ENCODING_UTF8);

	if (!result)
	{
		LOG_DEBUG("Load xml file failed!");
		return;
	}

	auto root = doc.RootElement();
	if (root == nullptr)
	{
        LOG_DEBUG("Root is null!");
		return;
	}

    auto element = root->FirstChild(OFFEN_USE_EXCEL);
    if (element == nullptr)
    {
        element = new TiXmlElement(OFFEN_USE_EXCEL);
        root->LinkEndChild(element);
    }

	for (auto child = element->FirstChildElement(); child != nullptr; child = child->NextSiblingElement())
	{
		if (child->ValueStr() == "use" && child->GetText() == winrt::to_string(name))
		{
			element->RemoveChild(child);
			break;
		}
	}

	doc.SaveFile(xmlPath);
}

bool Utility::IsOffenUse(const winrt::hstring& name)
{
	for (auto& value : offenUseExcelList)
	{
		if (value == name)
		{
			return true;
		}
	}
	return false;
}

#pragma endregion


#pragma region Process

void Utility::StartProcess(std::wstring commandLine) {

	// 创建启动信息结构体
	STARTUPINFO startupInfo;
	PROCESS_INFORMATION processInfo;
	ZeroMemory(&startupInfo, sizeof(startupInfo));
	startupInfo.cb = sizeof(startupInfo);
	ZeroMemory(&processInfo, sizeof(processInfo));


	// 启动进程
    if (CreateProcess(
		NULL,                   // 应用程序名称
		&commandLine[0],        // 命令行
		NULL,                   // 进程安全属性
		NULL,                   // 线程安全属性
		FALSE,                  // 是否继承句柄
		CREATE_NO_WINDOW,       // 创建标志，避免弹出黑框
		NULL,                   // 环境变量
		NULL,                   // 当前目录
		&startupInfo,           // 启动信息
		&processInfo            // 进程信息
	)) {
		// 等待进程结束
		WaitForSingleObject(processInfo.hProcess, INFINITE);

		// 关闭进程和线程句柄
		CloseHandle(processInfo.hProcess);
		CloseHandle(processInfo.hThread);
	}
	else
	{
        DWORD err = GetLastError();
        LOG_ERROR((std::ostringstream() << "CreateProcess failed, error=" << static_cast<int>(err)));
        NotifyUiError(L"启动外部程序失败，错误码：" + std::to_wstring(err));
	}
}

#pragma endregion

#pragma region Process overload

void Utility::StartProcess(const std::string& commandLine)
{
    StartProcess(string_to_wstring(commandLine));
}

void Utility::StartProcessAsync(const std::string& commandLine)
{
    std::thread([commandLine]() { StartProcess(commandLine); }).detach();
}

void Utility::StartProcessAsync(const std::wstring& commandLine)
{
    std::thread([commandLine]() { StartProcess(commandLine); }).detach();
}
void Utility::SetUiErrorHandler(std::function<void(const winrt::hstring&)> handler)
{
    s_uiErrorHandler = std::move(handler);
}


#pragma endregion

#pragma region SVN Op


void Utility::RunTortoiseSVNCommand(const std::string& command, const std::string& path)
{
    const std::string exe = tortoiseSVNPath.empty() ? "TortoiseProc.exe" : (std::string("\"") + tortoiseSVNPath + "\"");
    const std::string fullCommand = exe + " /command:" + command + " /path:\"" + path + "\"";
    StartProcessAsync(fullCommand);

}

void Utility::RunTortoiseSVNMerge(const std::string& source, const std::string& target)
{
    const std::string exe = tortoiseSVNPath.empty() ? "TortoiseProc.exe" : (std::string("\"") + tortoiseSVNPath + "\"");
    const std::string fullCommand = exe + " /command:merge /path:\"" + target + "\" /fromurl:\"" + source + "\"";
    StartProcessAsync(fullCommand);
}


void Utility::CopySvnLog(const std::string& author, std::function<void(winrt::hstring)> callback)
{
    auto asnycTask = [&author, callback]()
    {
		// 获取今天的日期
		SYSTEMTIME sys;
		GetLocalTime(&sys);

		char date[20];
		sprintf_s(date, "%04d-%02d-%02d", sys.wYear, sys.wMonth, sys.wDay);

		// 添加一天
		auto tp = SystemTimeToTimePoint(sys);
		tp += std::chrono::hours(24);
		SYSTEMTIME newSys = TimePointToSystemTime(tp);

		char date2[20];
		sprintf_s(date2, "%04d-%02d-%02d", newSys.wYear, newSys.wMonth, newSys.wDay);


		std::ostringstream oss;


		for (auto& pair : urlMap)
		{
			if (ContainsIgnoreCase(pair.first, "dev"))
			{
				continue;
			}

			// 构建 SVN 命令
			std::string& url = pair.second;

			std::string command{ "cmd.exe /C svn log " + url + " --search \"" 
				+ author 
                + "\" --revision \"{" + date + "}:{" + date2 + "}\" > \"" + svnLogPath + "\""
			};

            // 执行命令并等待完成
            StartProcess(command);

			// 读取输出文件
			std::ifstream logFile(svnLogPath);

			std::unordered_set<std::string> hashSet;
			if (logFile.is_open())
			{
				std::string line;
				while (std::getline(logFile, line))
				{
					//std::string pattern = R"((?<Region>【.+?】)\s*(?<Theme>【.+?】)\s*(?<Content>[^-]+)(\s*--.*)?)";
					std::string pattern = R"((【.+?】)\s*(【.+?】)\s*([^-]+)(\s*--.*)?)";

					std::regex regex(pattern);
					std::smatch matches;

					if (std::regex_search(line, matches, regex))
					{
						if (matches.size() >= 4)
						{
							const auto& tag = matches[1].str() + matches[2].str();
							if (hashSet.find(tag) != hashSet.end())
							{
								continue;
							}
							hashSet.insert(tag);
							oss << tag + matches[3].str() << std::endl;
						}
						else if (matches.size() > 0)
						{
							oss << line << std::endl;
						}
					}
				}
				logFile.close();
			}
		}
		
		std::string textToCopy = oss.str();
		if (textToCopy.empty())
		{
			if (callback != nullptr)
			{
				callback(winrt::hstring{});
			}
			return;
		}


		if (callback != nullptr)
		{
			callback(ansi_to_hstring(textToCopy));
		}
    };



    std::thread(asnycTask).detach();
}

#pragma endregion


#pragma region VCS Unified Ops

static std::unique_ptr<IVersionControlClient> CreateVcsClient()
{
    return VersionControlFactory::Create(Utility::currentVcsType,
                                         Utility::tortoiseSVNPath,
                                         Utility::tortoiseGitPath,
                                         Utility::svnCliPath,
                                         Utility::gitCliPath);
}

void Utility::RunVcsUpdate(const std::string& path)
{
    auto client = CreateVcsClient();
    client->Update(path);
}

void Utility::RunVcsCommit(const std::string& path)
{
    auto client = CreateVcsClient();
    client->Commit(path);
}

void Utility::RunVcsLog(const std::string& path)
{
    auto client = CreateVcsClient();
    client->Log(path);
}

void Utility::RunVcsRevert(const std::string& path)
{
    auto client = CreateVcsClient();
    client->Revert(path);
}

void Utility::RunVcsCleanup(const std::string& path)
{
    auto client = CreateVcsClient();
    client->Cleanup(path);
}

void Utility::RunVcsMerge(const std::string& source, const std::string& target)
{
    auto client = CreateVcsClient();
    client->Merge(source, target);
}

#pragma endregion


#pragma region SearchExcel

std::filesystem::path Utility::GetSearchRoot()
{
	auto it = projPathMap.find(lastSearchExcelProj);
	if (it == projPathMap.end())
	{
        LOG_WARN("Project path not found!");
		return std::filesystem::path();
	}

	std::filesystem::path projRootPath = it->second;
	return projRootPath / data2Path;

}

static void GetAllExcelFiles(const std::filesystem::path& directory, std::vector<std::filesystem::path>& files)
{
    namespace fs = std::filesystem;
    try
    {
        for (fs::recursive_directory_iterator it(
                 directory,
                 fs::directory_options::skip_permission_denied),
             end;
             it != end;
             ++it)
        {
            const auto& entry = *it;
            std::error_code ec;
            if (!entry.is_regular_file(ec))
            {
                continue;
            }

            const auto extension = entry.path().extension();
            if (extension == ".xlsm" || extension == ".xls" || extension == ".xlsx")
            {
                files.push_back(entry.path());
            }
        }
    }
    catch (const std::exception&)
    {
        // 忽略枚举异常，尽可能收集可访问文件
    }
}

static std::wstring TrimKeyword(const std::wstring& keyword)
{
	std::wstring result = keyword;

	// 去除结尾的 "Data"
	const std::wstring suffix = L"data";
	if (result.size() >= suffix.size() && result.compare(result.size() - suffix.size(), suffix.size(), suffix) == 0)
	{
		result.erase(result.size() - suffix.size());
	}

	// 去除开头的 "cfg_"
	const std::wstring prefix = L"cfg_";
	if (result.size() >= prefix.size() && result.compare(0, prefix.size(), prefix) == 0)
	{
		result.erase(0, prefix.size());
	}

	return result;
}

std::vector<winrt::hstring> Utility::SearchExcel(std::filesystem::path& searchRoot, const std::wstring& keyword)
{
    std::vector<winrt::hstring> result;
    auto keywordTrimed = TrimKeyword(ToLower(keyword));

    std::vector<std::filesystem::path> filePaths;
    GetAllExcelFiles(searchRoot, filePaths);

    std::mutex resultMutex;
    std::atomic<size_t> index{0};
    const unsigned int hw = (std::max)(1u, std::thread::hardware_concurrency());
    const unsigned int maxWorkers = (std::min)(8u, hw);
    std::vector<std::thread> workers;
    workers.reserve(maxWorkers);

    auto worker = [&]() {
        while (true)
        {
            size_t i = index.fetch_add(1);
            if (i >= filePaths.size()) break;
            const auto& path = filePaths[i];

            std::error_code ec;
            auto relPath = std::filesystem::relative(path, searchRoot, ec).wstring();
            if (ec)
            {
                relPath = path.filename().wstring();
            }

            if (ContainsIgnoreCase(relPath, keywordTrimed))
            {
                std::lock_guard<std::mutex> lock(resultMutex);
                result.push_back(winrt::hstring(relPath));
                continue;
            }

            std::unique_ptr<libxl::Book, void(*)(libxl::Book*)> book(xlCreateXMLBook(), [](libxl::Book* p){ if (p) p->release(); });
            if (!book)
            {
                continue;
            }

            if (!(book->loadInfo(path.c_str())))
            {
                continue;
            }

            const int sheetCount = book->sheetCount();
            for (int si = 0; si < sheetCount; ++si)
            {
                const wchar_t* name = book->getSheetName(si);
                if (name == nullptr) { continue; }
                if (ContainsIgnoreCase(std::wstring(name), keywordTrimed))
                {
                    std::lock_guard<std::mutex> lock(resultMutex);
                    result.push_back(winrt::hstring(relPath));
                    break;
                }
            }
        }
    };

    for (unsigned int t = 0; t < maxWorkers; ++t)
    {
        workers.emplace_back(worker);
    }
    for (auto& th : workers) th.join();

    return result;
}

#pragma endregion

void Utility::OpenFolder(const std::string& folderPath)
{
    const std::string fullCommand = std::string("explorer.exe \"") + folderPath + "\"";
    StartProcessAsync(fullCommand);

}

void Utility::OpenXlsmWithExcel(const std::filesystem::path path)
{
	auto asyncTask = [path]()
		{
            // 使用 cmd.exe /C start 打开文件，兼容默认程序
            // 注意加上空标题参数 "" 以及对路径进行引号包裹
            const std::string fullCommand = std::string("cmd.exe /C start \"\" \"") + path.string() + "\"";
            StartProcess(fullCommand);


		};

	std::thread(asyncTask).detach();
}


#pragma region Log
// 日志函数已迁移到 Logging 模块，请直接调用 Logging::Log / Logging::LogToFile





#pragma endregion
